package code;

import java.lang.reflect.Array;
import java.util.ArrayList;

public class TrieNodo<T1, T2> {

	//private ArrayList<TrieNodo<T1, T2>> hijos;
	private TrieNodo<T1, T2>[] hijos;
	
	private ArrayList<T1> nombre;
	private Tupla<T1[], T2> clave;
	private T1[] alfabeto;

	public TrieNodo(){
		nombre = new ArrayList<T1>();
	}
	
	@SuppressWarnings("unchecked")
	public TrieNodo(T1[] alfab) {
		this();
		alfabeto = alfab;
		//hijos = new ArrayList<TrieNodo<T1, T2>>(alfab.length);
		hijos = (TrieNodo<T1, T2>[]) Array.newInstance(TrieNodo.class, alfab.length);
	}

	public void agregar(Tupla<T1[], T2> clave) throws Exception {
		TrieNodo<T1, T2> actual = this;
		ArrayList<T1> nombreActual = new ArrayList<T1>();
		for (int i = 0; i < clave.getE1().length; i++) {
			int pos = getIndex(clave.getE1()[i]);
			nombreActual.add(clave.getE1()[i]);
			
			//Si esta vacio, lo agrego
			try {
				if (actual.hijos[pos] == null){
					TrieNodo<T1, T2> nuevo = new TrieNodo<T1, T2>(alfabeto);
					nuevo.nombre.addAll(nombreActual);
					actual.hijos[pos] = nuevo;
				}
				actual = actual.hijos[pos];
			} catch (IndexOutOfBoundsException e) {
				throw new IndexOutOfBoundsException("Se intento ingresar una clave con un valor fuera del alfabeto propocionado. Clave: " 
						+ clave.getE1()[i].toString());
			}
			
			
		}
		actual.clave = clave;
	}

	public ArrayList<Tupla<T1 [],T2 >> buscar(T1[] clave){
		TrieNodo<T1, T2> actual = this;
		for (int i = 0; i < clave.length; i++) {
			int pos = getIndex(clave[i]);
			
			try {
				if (actual.hijos[pos] == null){
					return null;
				}
				actual = actual.hijos[pos];
			} catch (IndexOutOfBoundsException e) {
				//Manejo esta excepcion para cuando el usuario ingrese una clave
				//de busqueda que no se encuentre en el alfabeto proporcionado.
				return null;
			}
			
		}
		
		//Aqui nos encontramos con la variable 'actual' posicionada
		//en el ultimo nodo posible del termino de busqueda
		return actual.getChilds();
	}
	
	public ArrayList<Tupla<T1 [],T2 >> getChilds(){

		ArrayList<Tupla<T1[], T2>> resul = new ArrayList<Tupla<T1[], T2>>();

		if (clave != null) {
			resul.add(clave);
		}
		
		for (TrieNodo<T1, T2> tu : hijos) {
			if (tu != null)
				resul.addAll(tu.getChilds());
		}
		return resul;
	}
	
	
	
	public int getIndex(T1 valor) {
		if (valor instanceof Character){
			if (((Character) valor).equals(' '))
				return 26;
			return (Character) valor - 'a';	
		}
		else if (valor instanceof Integer)
			return (Integer) valor;
		else if (valor instanceof Long)
			return ((Long) valor).intValue();
		else
		return 0;
	}

}
